﻿using System.Collections.Generic;
using System.Linq;

namespace BuzzX8.Xmpp.StreamElements.Builders
{
    public abstract class CompositeElementBuilder<TElement, TItem> : StreamElementBuilder<TElement> where TElement : CompositeElement<TItem>
    {
        private ICollection<StreamElementBuilder> childBuilders;
        private StreamElementBuilder currentChildBuilder;

        protected bool IsChildBuilderUsed
        {
            get { return currentChildBuilder != null; }
        }

        protected ICollection<StreamElementBuilder> ChildBuilders
        {
            get { return childBuilders ?? (childBuilders = CreateChildBuilders()); }
        }

        protected virtual ICollection<StreamElementBuilder> CreateChildBuilders()
        {
            return new StreamElementBuilder[0];
        }

        protected override void AddAttributeToRootElement(string name, string value)
        {
            if (name == "xmlns") RootElement.ContentNamespace = value;
        }

        protected override void AddAttributeToCurrentNode(string name, string value)
        {
            if (IsChildBuilderUsed) currentChildBuilder.AddAttribute(name, value);
            else AddAttributeToEmbeddedNode(name, value);
        }

        protected virtual void AddAttributeToEmbeddedNode(string name, string value)
        { }

        public override void StartNode(string name)
        {
            base.StartNode(name);
            if (IsChildBuilderUsed)
            {
                currentChildBuilder.StartNode(name);
                return;
            }
            if (HasBuilderForNode(name))
            {
                currentChildBuilder = GetBuilderForNode(name);
                currentChildBuilder.StartNewElement(name);
            }
        }

        protected override void SetCurrentNodeValue(string value)
        {
            if (IsChildBuilderUsed) currentChildBuilder.SetNodeValue(value);
            else SetEmbeddedNodeValue(CurrentNodeName, value);
        }

        protected virtual void SetEmbeddedNodeValue(string nodeName, string value)
        { }

        public override void EndNode()
        {
            base.EndNode();
            if (IsChildBuilderUsed) currentChildBuilder.EndNode();
        }

        private bool HasBuilderForNode(string nodeName)
        {
            return ChildBuilders.Any(b => b.CanBuildElement(nodeName));
        }

        private StreamElementBuilder GetBuilderForNode(string name)
        {
            return ChildBuilders.First(b => b.CanBuildElement(name));
        }
    }

    public abstract class CompositeElementBuilder<T> : CompositeElementBuilder<T, StreamElement>
        where T : CompositeElement<StreamElement>
    {
    }
}
